package org.looop.studio.bean.factory.impl;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.looop.studio.bean.builder.BeanBuilder;
import org.looop.studio.bean.config.scope.BeanScopeConfig;
import org.looop.studio.bean.domain.BeanDefinition;
import org.looop.studio.bean.factory.BeanFactory;
import org.looop.studio.bean.factory.callback.BeanFactoryAware;
import org.looop.studio.bean.factory.callback.BeanFactoryInitialize;
import org.looop.studio.bean.scope.BeanScope;
import org.looop.studio.bean.scope.impl.PrototypeScope;
import org.looop.studio.bean.scope.impl.SingletonScope;
import org.looop.studio.bean.util.BeanDefinitionUtil;

public class DefaultBeanFactory implements BeanFactory {

	public DefaultBeanFactory(BeanDefinition[] beanDefinitions) {
		super();
		this.idToBeanDefinitionMap = BeanDefinitionUtil
				.convertToBeanDefinitionMap(beanDefinitions);
		this.allBeanDefinition = beanDefinitions;
	}

	private Map<String, BeanDefinition> idToBeanDefinitionMap;
	
	private BeanDefinition[] allBeanDefinition;
	
	private Map<String, BeanScope> beanScopeMap = new HashMap<String, BeanScope>();
	{
		registerBeanScope("singleton", new SingletonScope());
		registerBeanScope("prototype", new PrototypeScope());
	}
	
	@Override
	public Object getBean(String id) throws InstantiationException,
			IllegalAccessException, ClassNotFoundException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException {
		BeanDefinition beanDefinition = idToBeanDefinitionMap.get(id);
		if (beanDefinition == null) {
			return null;
		}
		return getBean(beanDefinition);
	}

	private Object getBean(BeanDefinition beanDefinition)
			throws InstantiationException, IllegalAccessException,
			ClassNotFoundException, InvocationTargetException,
			NoSuchMethodException {
		BeanScope beanScope = getBeanScope(beanDefinition);
		return beanScope.getBean(this, beanDefinition);
	}

	private BeanScope getBeanScope(BeanDefinition beanDefinition)
			throws InstantiationException, IllegalAccessException,
			ClassNotFoundException, InvocationTargetException,
			NoSuchMethodException {
		String scope = beanDefinition.getScope();
		if(scope == null || scope.length() == 0){
			scope="singleton";
		}
		BeanScope beanScope = beanScopeMap.get(scope);
		if(beanScope == null){
			getBeansByClassName(BeanScopeConfig.class);
			beanScope = beanScopeMap.get(beanDefinition.getScope());
		}
		return beanScope;
	}

	@Override
	public Object createBean(BeanDefinition beanDefinition)
			throws InstantiationException, IllegalAccessException,
			ClassNotFoundException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException {
		BeanDefinition beanDef = beanDefinition;
		BeanBuilder builder = beanDef.getBeanBuilder();
		Object bean = builder.buildBean(this, beanDef);
		if(bean instanceof BeanFactoryAware){
			((BeanFactoryAware)bean).setBeanFactory(this);
		}
		if(bean instanceof BeanFactoryInitialize){
			((BeanFactoryInitialize)bean).initBean();
		}
		return bean;
	}

	@Override
	public List getBeansByClassName(Class className) throws IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException {
		if(className == null){
			return Collections.emptyList();
		}
		List<Object> list = new ArrayList<Object>();
		for(BeanDefinition beanDef : allBeanDefinition){
			String beanClassName = beanDef.getClazz();
			if(Class.forName(beanClassName).isAssignableFrom(className)){
				list.add(getBean(beanDef));
			}
		}
		return list;
	}

	@Override
	public void registerBeanScope(String key, BeanScope scope) {
		beanScopeMap.put(key, scope);
	}
	
}
